package uk.ac.lkl.common.util.ds;



import uk.ac.lkl.common.util.ds.event.ListEvent;
import uk.ac.lkl.common.util.ds.event.ListListener;


/**
 * Composed on an abstract notifying list. Listens for changes and updates
 * itself appropriately.
 * 
 * Note: this class will not work properly with parent deletions if parent has
 * duplicates within list.
 * 
 * @author Darren Pearce
 * @version $Revision$
 * @version $Date$
 * 
 */
public class NotifyingSubList<E> extends AbstractNotifyingList<E> {

    private static final long serialVersionUID = 1L;


    private AbstractNotifyingList<E> parentList;


    public NotifyingSubList(AbstractNotifyingList<E> parentList) {
        super(parentList.getElementClass());
        this.parentList = parentList;
        addListListener();
    }


    private void addListListener() {
        parentList.addListListener(new ListListener<E>() {

            public void elementAdded(ListEvent<E> e) {
                processElementAdded(e);
            }


            public void elementRemoved(ListEvent<E> e) {
                processElementRemoved(e);
            }


            public void elementMoved(ListEvent<E> e) {
                processElementMoved(e);
            }

        });
    }


    private void processElementAdded(ListEvent<E> e) {
        // do nothing
    }


    private void processElementRemoved(ListEvent<E> e) {
        E element = e.getElement();
        remove(element);
    }


    private void processElementMoved(ListEvent<E> e) {
        // do nothing
    }


    /**
     * Add the element at the given parent index.
     * 
     * @param parentIndex
     * @return
     * 
     */
    public boolean addElementByParentIndex(int parentIndex) {
        E element = parentList.get(parentIndex);
        return add(element);
    }


    public int getParentIndex(E element) {
        return parentList.indexOf(element);
    }


    @Override
    public boolean add(E element) {
        if (!parentList.contains(element)) {
            // todo: may want to return false immediately. Make this depend on a
            // flag perhaps
            if (!parentList.add(element))
                return false;
        }

        int index = size();
        super.add(element);
        fireElementAdded(element, index);
        return true;
    }


    @Override
    public void add(int index, E element) {
        if (!parentList.contains(element)) {
            // todo: may want to return false immediately. Make this depend on a
            // flag perhaps
            if (!parentList.add(element))
                return;
        }

        super.add(index, element);
        fireElementAdded(element, index);

        for (int i = index; i < size() - 1; i++) {
            E movedElement = get(i);
            fireElementMoved(movedElement, i, i + 1);
        }
    }


    public boolean removeElementByParentIndex(int parentIndex) {
        E element = parentList.get(parentIndex);
        return removeElement(element);
    }


    public boolean removeElement(E element) {
        int index = indexOf(element);

        if (index == -1)
            return false;

        remove(index);
        return true;
    }


    public E remove(int index) {
        E result = super.remove(index);
        fireElementRemoved(result, index);

        for (int i = index; i < size(); i++) {
            E movedElement = get(i);
            fireElementMoved(movedElement, i + 1, i);
        }
        return result;
    }


    @Override
    public boolean remove(Object element) {
        // calls remove element
        return removeElement((E) element);
    }

}
